#include "led_gpio.h"
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>

static int write_text_file(const char *path, const char *text)
{
    FILE *f = fopen(path, "w");
    if (!f) return -1;
    int ok = fputs(text, f) >= 0;
    fclose(f);
    return ok ? 0 : -1;
}

static int readlink_basename(const char *linkpath, char *out, size_t outsz)
{
    char buf[PATH_MAX];
    ssize_t n = readlink(linkpath, buf, sizeof(buf) - 1);
    if (n < 0) return -1;
    buf[n] = '\0';
    const char *base = strrchr(buf, '/');
    base = base ? base + 1 : buf;
    strncpy(out, base, outsz - 1);
    out[outsz - 1] = '\0';
    return 0;
}

int led_try_unbind_owner(const char *led_sysfs_name)
{
    if (!led_sysfs_name || !*led_sysfs_name) return -1;
    char base[PATH_MAX];
    snprintf(base, sizeof(base), "/sys/class/leds/%s", led_sysfs_name);

    char driver[NAME_MAX] = {0};
    char subsystem[NAME_MAX] = {0};
    char device[NAME_MAX] = {0};
    char linkpath[PATH_MAX];

    snprintf(linkpath, sizeof(linkpath), "%s/device/driver", base);
    if (readlink_basename(linkpath, driver, sizeof(driver)) != 0) return -1;

    snprintf(linkpath, sizeof(linkpath), "%s/device/subsystem", base);
    if (readlink_basename(linkpath, subsystem, sizeof(subsystem)) != 0) return -1;

    snprintf(linkpath, sizeof(linkpath), "%s/device", base);
    if (readlink_basename(linkpath, device, sizeof(device)) != 0) return -1;

    char unbind_path[PATH_MAX];
    snprintf(unbind_path, sizeof(unbind_path), "/sys/bus/%s/drivers/%s/unbind", subsystem, driver);
    if (write_text_file(unbind_path, device) != 0) return -1;
    usleep(100 * 1000);
    return 0;
}

int led_request_output_line(const char *chip_label,
                            unsigned int line_offset,
                            int active_low,
                            const char *consumer,
                            const char *led_sysfs_name,
                            struct gpiod_chip **out_chip,
                            struct gpiod_line **out_line)
{
    if (!out_chip || !out_line) return -1;
    *out_chip = NULL;
    *out_line = NULL;

    struct gpiod_chip *chip = gpiod_chip_open_by_label(chip_label);
    if (!chip) {
        fprintf(stderr, "open LED chip '%s' failed\n", chip_label ? chip_label : "NULL");
        return -1;
    }
    struct gpiod_line *line = gpiod_chip_get_line(chip, line_offset);
    if (!line) {
        fprintf(stderr, "get LED line %u failed\n", line_offset);
        gpiod_chip_close(chip);
        return -1;
    }

    unsigned int flags = 0u;
#ifdef GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW
    if (active_low)
        flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW;
#endif

    int init_phys = active_low ? 1 : 0; // 初始逻辑灭
    int tried_unbind = 0;
retry:
#ifdef gpiod_line_request_output_flags
    if (gpiod_line_request_output_flags(line, consumer ? consumer : "led-gpio", flags, init_phys) != 0) {
#else
    if (gpiod_line_request_output(line, consumer ? consumer : "led-gpio", init_phys) != 0) {
#endif
        int err = errno;
        if (err == EBUSY && led_sysfs_name && !tried_unbind) {
            fprintf(stderr, "Attempting to unbind %s and retry...\n", led_sysfs_name);
            if (led_try_unbind_owner(led_sysfs_name) == 0) {
                tried_unbind = 1;
                goto retry;
            }
        }
        perror("request LED line as output");
        gpiod_line_release(line);
        gpiod_chip_close(chip);
        return -1;
    }

    *out_chip = chip;
    *out_line = line;
    return 0;
}

int led_set_logical(struct gpiod_line *line, int active_low, int logical)
{
    if (!line) return -1;
    int phys = active_low ? !logical : logical;
    return gpiod_line_set_value(line, phys);
}

